/*
 * Created by kopper on 01.02.15 08:52
 * (C) Copyright 2015 kopperek@gmail.com
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 */

package app.android.kopper.gpxdownloader;

import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.AsyncTask;
import android.os.Environment;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import app.android.kopper.gpxdownloader.reader.GR245Reader;
import app.android.kopper.gpxdownloader.reader.MG650Reader;

public class DataReaderAsyncTask extends AsyncTask<UsbManager,ProgressParam,DataReaderResult>{

    private MainActivity activity;

    public void connectActivity(MainActivity activity) {
        this.activity=activity;
    }

    public void disconnectActivity() {
        this.activity=null;
    }

    @Override
    protected DataReaderResult doInBackground(UsbManager... params) {
        UsbDevice usbDevice=null;
        UsbDeviceConnection connection=null;
        try {
            publishProgress(new ProgressParam(activity.getString(R.string.progress_start),"",0,0));
            usbDevice=openDevice(params[0],new HashSet<UsbDeviceDef>(Arrays.asList(
                    new UsbDeviceDef(1155,22336,new MG650Reader()),
                    new UsbDeviceDef(4292,60000,new GR245Reader())
            )));
            connection=usbDevice.openConnection();
            if(connection==null)
                throw new IOException(activity.getString(R.string.error_communication));

            final File parentDir=createDirectory(Environment.getExternalStorageDirectory(),"GpxDownloader");
            final String importDirName=new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());

            long start=System.currentTimeMillis();

            String addMessage=usbDevice.getUsbDeviceDef().getReader().performReading(usbDevice,connection,new IUtilProvider() {
                @Override
                public void publishProgress(ProgressParam progressParam) {
                    DataReaderAsyncTask.this.publishProgress(progressParam);
                }

                @Override
                public File createFile(String fileName) throws IOException {
                    File importDir=createDirectory(parentDir,importDirName);
                    File rawFile=new File(importDir,fileName);
                    if(!rawFile.createNewFile())
                        throw new IOException(activity.getString(R.string.error_can_not_create_file));
                    return (rawFile);
                }

                @Override
                public String getString(int id) {
                    return activity==null?"":activity.getString(id);
                }
            });
            StringBuffer result=new StringBuffer(
                    activity.getString(R.string.progress_task_completed)+"\n\n"+
                            activity.getString(R.string.progress_task_completed_result_localization)+
                            " "+parentDir.getAbsolutePath()+"/"+importDirName+"\n");
            long time=(System.currentTimeMillis()-start)/1000;
            result.append(String.format(
                    activity.getString(R.string.progress_task_completed_time_consumed)+
                            " %ds (%02d:%02d:%02d)",time,(time/60/60),(time/60)%60,(time%60)));
            if(addMessage!=null)
                result.append("\n"+addMessage);
            return new DataReaderResult(result.toString(),null);
        } catch(IOException e) {
            LogProxy.e("",e);
            return (new DataReaderResult("",e));
        } finally {
            if(usbDevice!=null&&connection!=null) {
                connection.close();
                usbDevice.close(connection);
            }
        }
    }

    private File createDirectory(File parent,String name) throws IOException {
        File file=new File(parent,name);
        if(!file.exists()) {
            if(!file.mkdirs())
                throw new IOException(activity.getString(R.string.error_directory_not_created)+" ("+name+")");
        } else {
            if(!file.isDirectory())
                throw new IOException(activity.getString(R.string.error_not_directory)+" ("+name+")");
        }
        return(file);
    }

    private app.android.kopper.gpxdownloader.UsbDevice openDevice(UsbManager manager, Set<UsbDeviceDef> validDevices) throws IOException {
        android.hardware.usb.UsbDevice device=null;
        UsbDeviceDef selectedDef=null;
        for(UsbDeviceDef def:validDevices) {
            device=findDevice(manager,def.getVendorId(),def.getProductId());
            if(device!=null) {
                selectedDef=def;
                break;
            }
        }


        if(device==null)
            throw new IOException(activity.getString(R.string.error_device_not_found));

        UsbInterface intf=null;
        UsbEndpoint output=null;
        UsbEndpoint input=null;

        for(int i=0;i<device.getInterfaceCount();i++) {

            UsbInterface usbInterface=device.getInterface(i);

            if(usbInterface.getInterfaceClass()==UsbConstants.USB_CLASS_CDC_DATA || usbInterface.getInterfaceClass()==UsbConstants.USB_CLASS_VENDOR_SPEC) {
                intf=usbInterface;

                for(int j=0;j<intf.getEndpointCount();j++) {
                    UsbEndpoint endpoint=intf.getEndpoint(j);

                    if(endpoint.getType()==UsbConstants.USB_ENDPOINT_XFER_BULK) {
                        if(endpoint.getDirection()==UsbConstants.USB_DIR_OUT)
                            output=endpoint;
                        if(endpoint.getDirection()==UsbConstants.USB_DIR_IN)
                            input=endpoint;
                    }
                }
            }
        }
        if(input==null||output==null)
            throw new IOException(activity.getString(R.string.error_no_valid_endpoints));
        return(new UsbDevice(manager,device,input,output,intf,selectedDef));
    }

    private android.hardware.usb.UsbDevice findDevice(UsbManager manager,int vendorId,int productId) {
        HashMap<String,android.hardware.usb.UsbDevice> deviceList=manager.getDeviceList();
        for(android.hardware.usb.UsbDevice device:deviceList.values()) {
            if(device.getVendorId()==vendorId && device.getProductId()==productId)
                return(device);
        }
        return null;
    }


    @Override
    protected void onProgressUpdate(ProgressParam... values) {
        activity.updateProgress(values[0]);
    }

    @Override
    protected void onPostExecute(DataReaderResult result) {
        activity.done(result);
    }




}
